
#include <sstream>
#include "util.h"
#include "context.h"
#include "ActionBase.h"
#include "usermgr.h"
#include "matchordermgr.h"

static std::int64_t GetMoneyFromKb(double balance)
{
    return 1000 * balance;
}
static double GetMoneyToKb(std::int64_t balance)
{
    return balance / 1000.0;
}

#define ERROR_CONCAT_STRING_PARAM1(error_msg, formatStr, param)   \
    {                                           \
        char errorBuffer[128] = {0};            \
        sprintf(errorBuffer, formatStr, param); \
        error_msg = errorBuffer;                \
    }

static std::string ErrToString(EKBEvent_Error ec_code, std::int32_t param1 = 0)
{
    std::string error_msg = "";
    switch (ec_code)
    {
        case KB_ERR_NONE: error_msg = "Success"; break;
        case KB_ERR_INVALID_UID: ERROR_CONCAT_STRING_PARAM1(error_msg, "uid:%d is invalid", param1); break;
        case KB_ERR_INVALID_ORDER: error_msg = "invalid order"; break;
        case KB_ERR_INNER_ERROR: error_msg = "net error"; break;
        case KB_ERR_NOTENOUGHBALANCE: ERROR_CONCAT_STRING_PARAM1(error_msg, "not enough balance ret : %d", param1);break;
        case KB_ERR_INNER_DB_ERROR: error_msg = "db error"; break;
        case KB_ERR_ALREADY_MATCHREG: error_msg = "alread matchreg"; break;
        case KB_ERR_ALREADY_MATCHUNREG: error_msg = "alread matchunreg"; break;
        case KB_ERR_ALREADY_MATCHSETTLE: error_msg = "alread matchsettle"; break;
        case KB_ERR_NOT_MATCHREG: error_msg = "not matchreg"; break;
        case KB_ERR_NOT_MATCHORDER: error_msg = "match order error"; break;

        default: error_msg = "unknowed errcode"; break;
    }
    return error_msg;
}

class ActionGameMatchApi
{
public:
    static bool Init()
    {
        Ctx.msg_router.BindHttpHandle("post", "/matchReg", ActionGameMatchApi::MatchReg);
        Ctx.msg_router.BindHttpHandle("post", "/matchUnReg", ActionGameMatchApi::MatchUnReg);
        Ctx.msg_router.BindHttpHandle("post", "/matchSettlement", ActionGameMatchApi::MatchSettlement);
        Ctx.msg_router.BindHttpHandle("post", "/matchReward", ActionGameMatchApi::MatchReward);
        return true;
    }
    
protected:
    static void Response(std::uint32_t request_id, EKBEvent_Error err, const std::string& username, const std::string& currency, std::int64_t balance)
    {
        Json::Value json;
        json["username"] = username;
        json["balance"] = balance / 1000.0;
        json["currency"] = currency;
        json["errorCode"] = (int)err;
        json["message"] = ErrToString(err);
        std::map<std::string,std::string> heads;
        heads["Content-Type"] = "application/json";
        Ctx.msg_router.httpServerBack(request_id, 200, heads, CTools::ToStr(json));
    }

public:

    static void MatchReg(HttpData&& http_data)
    {
        EKBEvent_Error err = KB_ERR_NONE;
        std::string err_msg = "";
        std::string username = "";
        std::string currency = "";
        std::int64_t balance = 0;
        do
        {
            log_debug("request_id:%d,ip:%s,target:%s,body:%s", http_data.request_id, http_data.ip.c_str(), http_data.target.c_str(), http_data.body.c_str());

            std::uint64_t request_time = CTools::GetUnixTimeMs();
            Json::Value req;
            Json::Reader reader;
            if (!reader.parse(http_data.body, req))
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "parser json failed";
                break;
            }
            if (!req.isObject()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "req is not json object";
                break;
            }
        
            std::string game;
            std::uint32_t gameid;
            std::int32_t matchtype = 0;
            std::uint64_t sessionid = 0;
            std::string matchid;
            std::string order;
            std::int64_t fee = 0;
            std::int32_t matchtime = 0;
        
            if (!req.isMember("username") || !req["username"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "username not exist or not string type";
                break;
            }
            username = req["username"].asString();
        
            if (!req.isMember("currency") || !req["currency"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "currency not exist or not string type";
                break;
            }
            currency = req["currency"].asString();
            if (!req.isMember("game") || !req["game"].isIntegral()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "game not exist or not int type";
                break;
            }
            game = std::to_string(req["game"].asInt());
            gameid = req["game"].asInt();
            if (!req.isMember("matchtype") || !req["matchtype"].isInt()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "matchtype not exist or not int type";
                break;
            }
            matchtype = req["matchtype"].asInt();
            
            if (!req.isMember("sessionId") || !req["sessionId"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "sessionId not exist or not string type";
                break;
            }
            sessionid = req["sessionId"].asInt64();
            matchid = std::to_string(sessionid);
        
            if (!req.isMember("round") || !req["round"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "round not exist or not isInt64 type";
                break;
            }
            order = std::to_string(req["round"].asInt64());
        
            if (!req.isMember("fee") || !req["fee"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "fee not exist or not double type";
                break;
            }
            //下注金额
            fee = GetMoneyFromKb( req["fee"].asDouble());
            if (!req.isMember("matchtime") || !req["matchtime"].isIntegral()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "matchtime not exist or not int typ";
                break;
            }
            matchtime = req["matchtime"].asInt();
        
            std::uint32_t uid = ::atoi(username.c_str());
            std::string cycle = matchid + ":" + std::to_string(uid);
            log_debug("uid:%u,game:%s,matchtype:%d,matchid:%s,order:%s,fee:%lld", uid, game.c_str(), matchtype, matchid.c_str(), order.c_str(), fee);

            if (uid <= 0) 
            {
                err = KB_ERR_INVALID_UID;
                err_msg = ErrToString(err, uid);
                break;
            }

            stUser* st_user = UserMgr::Instance()->Get(username);
            if (st_user == nullptr)
            {
                st_user = UserMgr::Instance()->GenUser("gamemock", currency, 9999999999, username);
                if (!st_user)
                {
                    err = KB_ERR_INVALID_UID;
                    log_error("GetUser failed, str::%s", http_data.body.c_str());
                    break;
                }
            }
            if (currency.compare(st_user->currency) != 0)
            {
                err = KB_ERR_INVALID_PARAM;
                log_error("currency match failed, str::%s", http_data.body.c_str());
                break;
            }
            if (st_user->balance < fee)
            {
                err = KB_ERR_NOTENOUGHBALANCE;
                log_error("money not enough, balance:%lld, str::%s", st_user->balance, http_data.body.c_str());
                break;
            }

            std::int32_t firstReg = 0;
            err = MatchOrderMgr::Instance()->CheckMatchReg(uid, "kb", sessionid, order, fee, matchtime, firstReg);
            if (err != KB_ERR_NONE)
            {
                err_msg = ErrToString(err);
                break;
            }

            balance = st_user->balance -= fee;

            if (firstReg == 1)
            {
                MatchOrderData order_data;
                order_data.uid = uid;
                order_data.game = gameid;
                order_data.matchtype = matchtype;
                order_data.currency = currency;
                order_data.regid = order;
                order_data.fee = fee;
                // 首次创建订单
                MatchOrderMgr::Instance()->CreateMatch(uid, sessionid, matchtime, "kb", std::move(order_data));
            }
            else
            {
                MatchOrderMgr::Instance()->MatchReg(uid, sessionid, matchtime, "kb", order, fee);
            }

        } while (false);

        log_debug("Handle over, id:%d, err:%d, username:%s", http_data.request_id, err, username.c_str());
        Response(http_data.request_id, err, username, currency, balance);
    }

    static void MatchUnReg(HttpData&& http_data)
    {
        EKBEvent_Error err = KB_ERR_NONE;
        std::string err_msg = "";
        std::string username = "";
        std::string currency = "";
        std::int64_t balance = 0;
       
        do
        {
            log_debug("request_id:%d,ip:%s,target:%s,body:%s", http_data.request_id, http_data.ip.c_str(), http_data.target.c_str(), http_data.body.c_str());
            std::uint64_t request_time = CTools::GetUnixTimeMs();
            Json::Value req;
            Json::Reader reader;
            if (!reader.parse(http_data.body, req))
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "parser json failed";
                break;
            }
            if (!req.isObject()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "req is not json object";
                break;
            }
        
            std::string game;
            std::int32_t matchtype = 0;
            std::uint64_t sessionid = 0;
            std::string matchid;
            std::string order;
            std::int64_t fee = 0;
            std::int32_t matchtime = 0;
        
            if (!req.isMember("username") || !req["username"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "username not exist or not string type";
                break;
            }
            username = req["username"].asString();
        
            if (!req.isMember("currency") || !req["currency"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "currency not exist or not string type";
                break;
            }
            currency = req["currency"].asString();
            if (!req.isMember("game") || !req["game"].isIntegral()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "game not exist or not int type";
                break;
            }
            game = std::to_string(req["game"].asInt());
        
            if (!req.isMember("matchtype") || !req["matchtype"].isInt()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "matchtype not exist or not int type";
                break;
            }
            matchtype = req["matchtype"].asInt();
            
            if (!req.isMember("sessionId") || !req["sessionId"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "sessionId not exist or not string type";
                break;
            }
            sessionid = req["sessionId"].asInt64();
            matchid = std::to_string(sessionid);
        
            if (!req.isMember("round") || !req["round"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "round not exist or not isInt64 type";
                break;
            }
            order = std::to_string(req["round"].asInt64());
        
            if (!req.isMember("fee") || !req["fee"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "fee not exist or not double type";
                break;
            }
            //下注金额
            fee = GetMoneyFromKb(req["fee"].asDouble());
            if (!req.isMember("matchtime") || !req["matchtime"].isIntegral()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "matchtime not exist or not int typ";
                break;
            }
            matchtime = req["matchtime"].asInt();
        
            std::uint32_t uid = ::atoi(username.c_str());
            std::string cycle = matchid + ":" + std::to_string(uid);
            log_debug("uid:%u,game:%s,matchtype:%d,matchid:%s,order:%s,fee:%lld", uid, game.c_str(), matchtype, matchid.c_str(), order.c_str(), fee);
            
            if (uid <= 0) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "uid:%d is invalid";
                break;
            }
           
            stUser* st_user = UserMgr::Instance()->Get(username);
            if (st_user == nullptr)
            {
                st_user = UserMgr::Instance()->GenUser("gamemock", currency, 9999999999, username);
                if (!st_user)
                {
                    err = KB_ERR_INVALID_UID;
                    log_error("GetUser failed, str::%s", http_data.body.c_str());
                    break;
                }
            }
            if (currency.compare(st_user->currency) != 0)
            {
                err = KB_ERR_INVALID_PARAM;
                log_error("currency match failed, str::%s", http_data.body.c_str());
                break;
            }
            if (st_user->balance < fee)
            {
                err = KB_ERR_NOTENOUGHBALANCE;
                log_error("money not enough, balance:%lld, str::%s", st_user->balance, http_data.body.c_str());
                break;
            }
        
            fee = 0;
            // 检测订单
            err = MatchOrderMgr::Instance()->CheckMatchUnReg(uid, "kb", sessionid, order, matchtime, fee);
            if (err != KB_ERR_NONE)
            {
                err_msg = ErrToString(err);
                break;
            }

            balance = st_user->balance += fee;

            MatchOrderMgr::Instance()->MatchUnReg(uid, sessionid, matchtime, "kb", order);
        } while (false);

        log_debug("Handle over, id:%d, err:%d, username:%s", http_data.request_id, err, username.c_str());
        Response(http_data.request_id, err, username, currency, balance);
    }

    static void MatchSettlement(HttpData&& http_data)
    {
        EKBEvent_Error err = KB_ERR_NONE;
        std::string err_msg = "";
        std::string username = "";
        std::string currency = "";
        std::int64_t balance = 0;

        do
        {
            log_debug("request_id:%d,ip:%s,target:%s,body:%s", http_data.request_id, http_data.ip.c_str(), http_data.target.c_str(), http_data.body.c_str());
            std::uint64_t request_time = CTools::GetUnixTimeMs();
            Json::Value req;
            Json::Reader reader;
            if (!reader.parse(http_data.body, req))
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "parser json failed";
                break;
            }
            if (!req.isObject()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "req is not json object";
                break;
            }
        
            std::string game;
            std::int32_t matchtype = 0;
            std::uint64_t sessionid = 0;
            std::string matchid;
            std::string order;
            std::int64_t fee = 0;
            std::int64_t winlose = 0;
            std::int64_t withdraw_limit = 0;
            std::int64_t valid_flow = 0;
            std::int32_t matchtime = 0;
        
            if (!req.isMember("username") || !req["username"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "username not exist or not string type";
                break;
            }
            username = req["username"].asString();
        
            if (!req.isMember("currency") || !req["currency"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "currency not exist or not string type";
                break;
            }
            currency = req["currency"].asString();
            if (!req.isMember("game") || !req["game"].isIntegral()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "game not exist or not int type";
                break;
            }
            game = std::to_string(req["game"].asInt());
        
            if (!req.isMember("matchtype") || !req["matchtype"].isInt()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "matchtype not exist or not int type";
                break;
            }
            matchtype = req["matchtype"].asInt();
            
            if (!req.isMember("sessionId") || !req["sessionId"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "sessionId not exist or not string type";
                break;
            }
            sessionid = req["sessionId"].asInt64();
            matchid = std::to_string(sessionid);
        
            if (!req.isMember("round") || !req["round"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "round not exist or not isInt64 type";
                break;
            }
            order = std::to_string(req["round"].asInt64());
        
            if (!req.isMember("fee") || !req["fee"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "fee not exist or not double type";
                break;
            }
            //下注金额
            fee = GetMoneyFromKb(req["fee"].asDouble());
            if (!req.isMember("reward") || !req["reward"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "reward not exist or not double type";
                break;
            }
            //结算金额
            winlose = GetMoneyFromKb(req["reward"].asDouble());
            if (!req.isMember("matchtime") || !req["matchtime"].isIntegral()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "matchtime not exist or not int typ";
                break;
            }
            matchtime = req["matchtime"].asInt();
        
            if (!req.isMember("withdrawlimit") || !req["withdrawlimit"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "withdrawlimit not exist or not double type";
                break;
            }
            //结算金额
            withdraw_limit = GetMoneyFromKb(req["withdrawlimit"].asDouble());
            
            if (!req.isMember("validflow") || !req["validflow"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "valid_flow not exist or not double type";
                break;
            }
            //结算金额
            valid_flow = GetMoneyFromKb(req["validflow"].asDouble());

            std::uint32_t uid = ::atoi(username.c_str());
            std::string cycle = matchid + ":" + std::to_string(uid);
            log_debug("uid:%u,game:%s,matchtype:%d,matchid:%s,order:%s,fee:%lld,winlose:%lld", uid, game.c_str(), matchtype, matchid.c_str(), order.c_str(), fee, winlose);
            
            if (uid <= 0) 
            {
                err = KB_ERR_INVALID_UID;
                err_msg = ErrToString(err, uid);
                break;
            }

            stUser* st_user = UserMgr::Instance()->Get(username);
            if (st_user == nullptr)
            {
                st_user = UserMgr::Instance()->GenUser("gamemock", currency, 9999999999, username);
                if (!st_user)
                {
                    err = KB_ERR_INVALID_UID;
                    log_error("GetUser failed, str::%s", http_data.body.c_str());
                    break;
                }
            }
            if (currency.compare(st_user->currency) != 0)
            {
                err = KB_ERR_INVALID_PARAM;
                log_error("currency match failed, str::%s", http_data.body.c_str());
                break;
            }
            if (st_user->balance < fee)
            {
                err = KB_ERR_NOTENOUGHBALANCE;
                log_error("money not enough, balance:%lld, str::%s", st_user->balance, http_data.body.c_str());
                break;
            }
        
            // 检测订单
            err = MatchOrderMgr::Instance()->CheckMatchSettlement(uid, "kb", sessionid, order, winlose, matchtime);
            if (err != KB_ERR_NONE)
            {
                err_msg = ErrToString(err);
                break;
            }

            balance = st_user->balance += winlose;

            MatchOrderMgr::Instance()->MatchSettlement(uid, sessionid, matchtime, "kb", order, winlose);
           
        } while (false);

        log_debug("Handle over, id:%d, err:%d, username:%s", http_data.request_id, err, username.c_str());
        Response(http_data.request_id, err, username, currency, balance);
    }

    static void MatchReward(HttpData&& http_data)
    {
        EKBEvent_Error err = KB_ERR_NONE;
        std::string err_msg = "";
        std::string username = "";
        std::string currency = "";
        std::int64_t balance = 0;

        do
        {
            log_debug("request_id:%d,ip:%s,target:%s,body:%s", http_data.request_id, http_data.ip.c_str(), http_data.target.c_str(), http_data.body.c_str());
            std::uint64_t request_time = CTools::GetUnixTimeMs();
            Json::Value req;
            Json::Reader reader;
            if (!reader.parse(http_data.body, req))
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "parser json failed";
                break;
            }
            if (!req.isObject()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "req is not json object";
                break;
            }
        
            std::string game;
            std::int32_t rewardid = 0;
            std::int32_t matchtype = 0;
            std::string order;
            std::int64_t reward = 0;
            std::int64_t withdraw_limit = 0;
            std::int32_t matchtime = 0;
        
            if (!req.isMember("username") || !req["username"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "username not exist or not string type";
                break;
            }
            username = req["username"].asString();
        
            if (!req.isMember("currency") || !req["currency"].isString()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "currency not exist or not string type";
                break;
            }
            currency = req["currency"].asString();

            if (!(!req.isMember("game") || !req["game"].isIntegral()))
            {
                game = std::to_string(req["game"].asInt());
            }
            if (!(!req.isMember("matchtype") || !req["matchtype"].isInt()))
            {
                matchtype = req["matchtype"].asInt();
            }
            if (!(!req.isMember("rewardid") || !req["rewardid"].isInt64()))
            {
                rewardid = req["matchid"].asInt64();
            }

            if (!req.isMember("round") || !req["round"].isInt64()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "round not exist or not isInt64 type";
                break;
            }
            order = std::to_string(req["round"].asInt64());
        
            if (!req.isMember("reward") || !req["reward"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "reward not exist or not double type";
                break;
            }
            reward = GetMoneyFromKb(req["reward"].asDouble());

            if (!(!req.isMember("matchtime") || !req["matchtime"].isIntegral()))
            {
                matchtime = req["matchtime"].asInt();
            }
        
            if (!req.isMember("withdrawlimit") || !req["withdrawlimit"].isDouble()) 
            {
                err = KB_ERR_INVALID_PARAM;
                err_msg = "withdrawlimit not exist or not double type";
                break;
            }
            //结算金额
            withdraw_limit = GetMoneyFromKb(req["withdrawlimit"].asDouble());
            
            std::uint32_t uid = ::atoi(username.c_str());
            log_debug("uid:%u,game:%s,matchtype:%d,rewardid:%d,order:%s,reward:%lld", uid, game.c_str(), matchtype, rewardid, order.c_str(), reward);

            if (uid <= 0) 
            {
                err = KB_ERR_INVALID_UID;
                err_msg = ErrToString(err, uid);
                break;
            }

            stUser* st_user = UserMgr::Instance()->Get(username);
            if (st_user == nullptr)
            {
                st_user = UserMgr::Instance()->GenUser("gamemock", currency, 9999999999, username);
                if (!st_user)
                {
                    err = KB_ERR_INVALID_UID;
                    log_error("GetUser failed, str::%s", http_data.body.c_str());
                    break;
                }
            }
            if (currency.compare(st_user->currency) != 0)
            {
                err = KB_ERR_INVALID_PARAM;
                log_error("currency match failed, str::%s", http_data.body.c_str());
                break;
            }
            
            balance = st_user->balance += reward;
       
        } while (false);

        log_debug("Handle over, id:%d, err:%d, username:%s", http_data.request_id, err, username.c_str());
        Response(http_data.request_id, err, username, currency, balance);
    }

};

static bool __init__ = ActionGameMatchApi::Init();

